package com.alibaba.wallstreet.uitls;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.List;

import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPFileEntryParser;
import org.apache.commons.net.ftp.FTPFileFilter;
import org.apache.commons.net.ftp.FTPListParseEngine;
import org.apache.commons.net.ftp.FTPSClient;
import org.apache.log4j.Logger;

import com.alibaba.wallstreet.exception.BizCommonException;
import com.alibaba.wallstreet.exception.CommonErrorCode;

/**
 * 类FtpClient.java的实现描述：FTP下载通用工具
 * 
 * @author zhiping.szp 2014-3-7 上午11:42:11
 */
public class FtpClient {

    protected static Logger logger = Logger.getLogger(FtpClient.class);

    private final FTPClient ftp;
    private final String    hostname;
    private final int       port;
    private String          userName;
    private String          passWord;

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassWord() {
        return passWord;
    }

    public void setPassWord(String passWord) {
        this.passWord = passWord;
    }

    public FTPClient getFtp() {
        return ftp;
    }

    public String getHostname() {
        return hostname;
    }

    public int getPort() {
        return port;
    }

    public FtpClient(String hostname, int port){
        this.ftp = new FTPClient();
        this.hostname = hostname;
        this.port = port;
    }

    public FtpClient(String hostname, int port, String protoal){
        this.ftp = new FTPSClient(protoal, false);
        this.hostname = hostname;
        this.port = port;
    }

    public int getLocalPort() {
        return ftp.getLocalPort();
    }

    /**
     * 连接FTP服务器
     * 
     * @return
     * @throws Exception
     */
    public void connect() {
        try {
            //设置120秒（2分钟）的连接超时
            ftp.setConnectTimeout(120 * 1000);   
            //设置读超时（阻塞时间）3分钟（60秒）,ftp数据链路的socket读超时时间
            ftp.setDataTimeout(180 * 1000);    
            //设置默认超时时间（ftp本身连接的超时时间,控制链路的socket超时时间）
            ftp.setDefaultTimeout(180 * 1000); 

            ftp.connect(this.hostname, this.port);
            ftp.setFileType(FTP.BINARY_FILE_TYPE);
        } catch (Exception e) {
            logger.error("connect exception.",e);
            ftpException("connect exception.", e);
        }
    }

    /**
     * 登陆到FTP服务器
     * 
     * @param userName
     * @param passWord
     * @return
     * @throws IOException
     * @throws Exception
     */
    public void login(String userName, String passWord) {
        this.userName = userName;
        this.passWord = passWord;
        try {
            if (!ftp.login(userName, passWord)) {
                throw new IOException(ftp.getReplyString());
            }
        } catch (Exception e) {
            logger.error("login exception.", e);
            ftpException("login exception.", e);
        }
    }

    public void disConnect() {
        try {
            if (ftp.isConnected()) {
                ftp.disconnect();
            }
        } catch (Exception e) {
            logger.error("ftp disConnect err",e);
        }
    }

    public void protWithP() {
        try {
            ((FTPSClient) ftp).execPROT("P");
        } catch (Exception e) {
            logger.error("protWithP exception.", e);
            ftpException("protWithP exception.", e);
        }
    }

    public void enterLocalPassiveMode() {
        ftp.enterLocalPassiveMode();
    }

    public List<String> listFiles(String path, String key, FTPFileFilter filter) {
        try {
            List<String> result = new ArrayList<String>();
            ftp.enterLocalPassiveMode();// 被动模式
            FTPListParseEngine engine = ftp.initiateListParsing(key, path);
            for (FTPFile ftpFile : engine.getFiles()) {
                if (filter == null || filter.accept(ftpFile)) {
                    result.add(ftpFile.getName());
                }
            }
            return result;
        } catch (Exception e) {
            logger.error("listFiles exception.", e);
            ftpException("listFiles exception.", e);
        }
        return new ArrayList<String>();
    }

    public List<String> listFiles(String path, Class<? extends FTPFileEntryParser> parser) {
        return listFiles(path, parser, null);
    }

    public List<String> listFiles(String path, Class<? extends FTPFileEntryParser> parser, FTPFileFilter filter) {
        return listFiles(path, parser.getName(), filter);
    }

    public List<String> listFiles(String path) {
        return listFiles(path, (String) null, null);
    }

    public List<String> listFiles() throws IOException {
        return listFiles(null, (String) null, null);
    }

    /**
     * 断开FTP服务器连接
     * 
     * @throws Exception
     */
    public void quit() {
        try {
            ftp.quit();
        } catch (Exception e) {
            logger.error("ftp quit err", e);
        }
    }

    /**
     * 获取当前FTP的工作目录
     * 
     * @return
     * @throws Exception
     */
    public String getWorkingDirectory() {
        try {
            return this.ftp.printWorkingDirectory();
        } catch (Exception e) {
            logger.error("getWorkingDirectory exception.", e);
            ftpException("getWorkingDirectory exception.", e);
        }
        return null;
    }

    private void ftpException(String msg, Exception e) {
        throw new BizCommonException("FTP: " + msg, e, CommonErrorCode.IO_EXCEPTION).set("host", hostname).set("port",
                                                                                                               port);
    }

    /**
     * 更改FTP服务器的工作目录
     * 
     * @param dir
     * @return
     * @throws Exception
     */
    public void changeWorkdingDirectory(String dir) {
        try {
            if (!ftp.changeWorkingDirectory(dir)) {
                throw new IOException(ftp.getReplyString());
            }
        } catch (Exception e) {
            logger.error("changeWorkdingDirectory exception.", e);
            ftpException("changeWorkdingDirectory exception.", e);
        }
    }

    /**
     * 下载FTP服务器的文件到本地目录
     * 
     * @param fullLocalFile 本地目录+文件名
     * @param fullRemoteFile FTP服务器根目录+文件名
     * @return
     * @throws Exception
     */
    public void get(String fullLocalFile, String fullRemoteFile) throws Exception {
        if (fullLocalFile.lastIndexOf(File.separator) > 0) {
            File localDir = new File(fullLocalFile.substring(0, fullLocalFile.lastIndexOf(File.separator)));
            if (!localDir.exists()) {
                if (!localDir.mkdir()) {
                    throw new BizCommonException("Can not make local dir : " + localDir, CommonErrorCode.IO_EXCEPTION);
                }
            }
        }

        File localFile = new File(fullLocalFile);
        OutputStream output = null;
        try {
            output = new BufferedOutputStream(new FileOutputStream(localFile));
            ftp.enterLocalPassiveMode();// 被动模式
            if (!this.ftp.retrieveFile(fullRemoteFile, output)) {
                throw new IOException(ftp.getReplyString());
            }
        } catch (IOException e) {
            logger.error("Ftp down file fail," + "get " + fullRemoteFile + " to " + fullLocalFile + " failure!", e);
            throw e;
        } catch (Exception e) {
            logger.error("Ftp down file fail,"+"get " + fullRemoteFile + " to " + fullLocalFile + " failure!", e);
            throw e;
        } 
        finally {
            if (output != null) {
                try {
                    output.close();
                } catch (IOException e) {
                    logger.error("close fail!", e);
                }
            }
        }
    }

    /**
     * 删除FTP的文件
     * 
     * @param remoteFile
     * @return
     * @throws Exception
     */
    public void delete(String remoteFile) {
        try {
            if (!this.ftp.deleteFile(remoteFile)) {
                throw new IOException(ftp.getReplyString());
            }
        } catch (Exception e) {
            logger.error("delete exception.", e);
            ftpException("delete exception.", e);
        }
    }

    /**
     * 上传本地文件到ftp服务器上
     * 
     * @param ftpFileName
     * @param localFile
     * @throws IOException
     */
    public boolean upload(String ftpFileName, File localFile) throws IOException {
        // File check.
        if (!localFile.exists()) {
            logger.error("Can't upload File '" + localFile.getAbsolutePath() + "'. This file doesn't exist.");
            return false;
        }
        // Upload.
        InputStream in = null;
        try {
            // Use passive mode to pass firewalls.
            ftp.enterLocalPassiveMode();// 被动模式
            ftp.setControlEncoding("UTF-8");
            in = new BufferedInputStream(new FileInputStream(localFile));
            return ftp.storeFile(ftpFileName, in);
        } finally {
            try {
                if (in != null) {
                    in.close();
                }
            } catch (IOException ex) {
                logger.error("close fail!", ex);
            }
        }
    }

    /**
     * 上传本地文件（文件流）到ftp服务器上
     * 
     * @param ftpFileName
     * @param localFile
     * @throws IOException
     */
    public void upload(String ftpFileName, InputStream localFile) throws IOException {
        // File check.
        if (localFile == null) {
            throw new IOException("Can't upload, this file inputStream doesn't exist.");
        }
        // Upload.
        try {
            // Use passive mode to pass firewalls.
            ftp.enterLocalPassiveMode();
            ftp.setControlEncoding("UTF-8");
            if (!ftp.storeFile(ftpFileName, localFile)) {
                throw new IOException("Can't upload file '" + ftpFileName
                                      + "' to FTP server. Check FTP permissions and path.");
            }
        } finally {
            try {
                localFile.close();
            } catch (IOException ex) {
                logger.error("close fail!", ex);
            }
        }
    }
}
